Imports
library(notly)
Attaching package: ‘notly’
The following object is masked from ‘package:plotly’:
ggplotly
View a fitness landscape
p <- plotFitnessLandscape()
save_dir <- file.path(output_dir, "FitnessFunctions")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "fitness_function.html")
htmlwidgets::saveWidget(as_widget(p), fname)
Plot different trait architectures according to different
algorithms
save_dir <- file.path(output_dir, "TraitArchitecture")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
# Additive effects
methodType <- "Additive"
g <- plotTraitArchitecture(founderPop, methodType)
ggplot2::ggsave(filename = paste0("traitarchitecture_", methodType, ".pdf"),
path=save_dir,
device = "pdf")
# Effect of each allele on fitness
methodType <- "Fitness"
g <- plotTraitArchitecture(founderPop, methodType)
ggplot2::ggsave(filename = paste0("traitarchitecture_", methodType, ".pdf"),
path=save_dir,
device = "pdf")
p <- plot3dPopulationFitness(founderPop, calculateFitnessTwoTrait)
fname <- file.path(save_dir, "3dpopulationfitness.html")
htmlwidgets::saveWidget(as_widget(p), fname)
Simulate several adaptive walks with different population sizes
# Different starting population sizes
subPopSizes <- c(50,500)
selProps <- c(0.4,0.4)
n.h2 <- 0.3
n.var <- 0.1
n.qtlPerChr <- 2
n.gens <- 50
# Don't use a SNP chip for these simulations
addSnpChip <- FALSE
fig <- plot_ly() %>%
layout(legend=list(title=list(text='Population Size')),
scene = list(xaxis = list(title = "Trait A"),
yaxis = list(title = "Trait B"),
zaxis = list(title = "Fitness"),
aspectmode='cube')) #hide_colorbar()
#source("Scripts/CreateFounderPop.R")
pop <- founderPop
for (gen in 1:n.burnInGens) {
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
}
# Iterate through each population size
for (p in 1:length(subPopSizes)) {
n.subPopSize <- subPopSizes[p]
n.selProp <- selProps[p]
print(n.subPopSize)
fit_df <- data.frame(gen=1:n.gens,
fitness=numeric(n.gens),
traitValA=numeric(n.gens),
traitValB=numeric(n.gens))
subPop <- selectInd(pop, nInd=n.subPopSize, use="rand")
# Iterate through the generations, update the result dataframe, and advance
# progeny based on the two trait fitness funcion
for(gen in 1:n.gens) {
meanFitness <- mean(twoTraitFitFunc(pheno(subPop)))
fit_df$fitness[gen] <- calculateFitnessTwoTraitModified(meanP(subPop)[1], meanP(subPop)[2])
fit_df$traitValA[gen] <- meanP(subPop)[1]
fit_df$traitValB[gen] <- meanP(subPop)[2]
selRat <- selectionRatio(meanFitness)
subPop <- selectCross(subPop, trait=twoTraitFitFunc, nInd=n.subPopSize*selRat, nCrosses=n.subPopSize)
}
# Add a trace to represent this population's adaptive walk
fig <- fig %>% add_trace(
fig,
fit_df,
name = n.subPopSize,
x = fit_df$traitValA,
y = fit_df$traitValB,
z = fit_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
line = list(autocolorscale=FALSE, colors="PuBuGn", color=p, which=2, width = 10)
)
}
[1] 50
[1] 500
# Create a matrix of fitness values, with a small increment along the x and y axes.
fitness_x = seq(n.initTraitVal*-1,n.initTraitVal*1, by=(n.initTraitVal/100))
fitness_y = seq(n.initTraitVal*-1,n.initTraitVal*1, by=(n.initTraitVal/100))
fitness_z = outer(fitness_x,fitness_y,calculateFitnessTwoTrait)
fig <- fig %>%
add_trace(
fig,
x=fitness_x,
y=fitness_y,
z=fitness_z,
type='surface',
colorbar=list(title = "Fitness"),
colors = viridis(n=10),
opacity=1.0)
fig
save_dir <- file.path(output_dir, "DifferentPopulationSizes")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, paste0("populationSizes_", subPopSizes[1], ",", subPopSizes[2], ",", subPopSizes[3], ".html"))
htmlwidgets::saveWidget(as_widget(fig), fname)
Overlay an adaptive walk over a fitness landscape, and save 3D and 2D
versions
fig <- plot_ly()
fit_df <- data.frame(gen=1:n.gens,
fitness=numeric(n.gens),
traitValA=numeric(n.gens),
traitValB=numeric(n.gens))
pop <- founderPop
for(gen in 1:n.gens) {
fit_df$fitness[gen] <- mean(twoTraitFitFunc(gv(pop)))
fit_df$traitValA[gen] <- meanG(pop)[1]
fit_df$traitValB[gen] <- meanG(pop)[2]
meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
selRat <- selectionRatio(meanFitness)
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=n.popSize*selRat, nCrosses=n.popSize)
}
save_dir <- file.path(output_dir, "OverlayAdaptiveWalk")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
plotType <- "CONTOUR"
pc <- overlayWalkOnLandscape(fit_df, type=plotType, calculateFitnessTwoTrait)
fname <- file.path(save_dir, paste0("adaptivewalk_", plotType, ".html"))
htmlwidgets::saveWidget(as_widget(pc), fname)
plotType <- "SURFACE"
ps <- overlayWalkOnLandscape(fit_df, type=plotType, calculateFitnessTwoTrait)
fname <- file.path(save_dir, paste0("adaptivewalk_", plotType, ".html"))
htmlwidgets::saveWidget(as_widget(ps), fname)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
Plot the change in allele frequency over time
n.qtlPerChr <- 2
source("Scripts/CreateFounderPop.R")
pop <- founderPop
# Results dataframe
freq.df <- data.frame(gen=1:n.gens)
# Get the effect sizes of each qtl
qtlEff.df <- getQtlEffectSizes(pop)
# Get the names of all the QTLs
qtl <- colnames(getUniqueQtl(pop))
# Create a dataframe of all zeros where the columns are the QTL ids, and the # rows is the # of generations
qtl.df <- data.frame(matrix(0, ncol=length(qtl), nrow=n.gens))
colnames(qtl.df) <- qtl
# Combine the dataframes
freq.df <- cbind(freq.df, qtl.df)
# Iterate through each generation
for(gen in 1:n.gens) {
meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
# Get the qtl genotype data
qtlGeno <- getUniqueQtl(pop)
# Get the frequency of the '2' allele at each locus
for (l in 1:length(qtl)) {
# id is the name of the qtl (chr_site)
id <- qtl[l]
# A list of genotype data for each individual in the population at that locus
locus <- qtlGeno[,id]
# Calculate the allele frequency as the frequency of homozygous individuals (for 'allele') +
# 1/2 * frequency of heterozygous individuals (assumes the locus is biallelic)
freq.df[gen,id] <- (sum(locus==n.allele)/n.popSize) + ((sum(locus==1)/n.popSize)/2)
}
# Determine selection ration based on fitness
selRat <- selectionRatio(meanFitness)
#Advance individuals
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=n.popSize*selRat, nCrosses=n.popSize)
}
# Make the dataframe tidy
freq.df<- melt(freq.df, id="gen", variable.name="QTL ID", value.name="Allele Frequency")
# Add the qtl effect size data to the dataframe
freq.df <- merge(freq.df, qtlEff.df, by.x="QTL ID", by.y="row.names", all.x=TRUE)
# Create a line plot for the change in frequency of alleles over time
# Each line's opacity is a function of its effect size (higher=darker)
g <- ggplot(freq.df, aes(x=gen, y=freq, color=id, alpha=eff_size)) +
geom_line(size=0.7)
save_dir <- file.path(output_dir, "AlleleFrequencies")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
ggplot2::ggsave(filename = paste0("allelefrequencies.pdf"),
path=save_dir,
device = "pdf",
width=10,
height=7)
Simulate an adaptive walk, and plot the following curve: #segregating
QTL/#segregating loci
Original question: out of segregating alleles left in the population,
how many of them would increase fitness? Is this even a valid
question?
TODO: - How does this change with size of allele? - How does size of
‘fitness delta’ change over generations? - Simualate mutations for a
single individual to figure out P(allelic substitution is favorable),
should reflect FKO - P(mutation gets fixed) - show that this matches
Kimura - Compare adaptive walks for DH/inbred population where there are
new mutations VS population w/standing genetic variation
n.gens <- 200
# Reset variables
source("Scripts/CreateFounderPop.R")
source("Scripts/GlobalParameters.R")
pop <- founderPop
df <- data.frame(gen=c(),
fitness=c(),
traitVal1=c(),
traitVal2=c(),
percFitInc=c(),
segAlleles=c(),
segQtl=c())
# Iterate through the generations
for (gen in 1:n.gens) {
print(gen)
# Counter variables
numHetLoci = 0
numHetQtl = 0
numInc = 0
# Get all of the loci from the population
segSiteGeno <- pullSegSiteGeno(pop)
# Get all of the QTL from the population (a sampling of the segSiteGeno)
qtlGeno <- getUniqueQtl(pop)
qtlLoci <- colnames(qtlGeno)
loci <- colnames(segSiteGeno)
nLoci <- ncol(segSiteGeno)
# Iterate through all of the loci
# TODO: just calculate numHetQtl / numHetLoci
for (l in 1:ncol(segSiteGeno)) {
id <- loci[l]
locus = segSiteGeno[,l]
# Check if the locus is segregating
if (hetLocus(locus)) {
# Increment the counter
numHetLoci <- numHetLoci + 1
# Determine the effect size - this is where the bug is
#e <- getEffectSize(locus, id, pop, "Fitness")
#if (e > 0) {
# numInc <- numInc +1
#}
# If the locus is a QTL, then it has an effect size
if (id %in% qtlLoci) {
numHetQtl <- numHetQtl + 1
}
}
}
# Calculate the ratio of segregating QTL to segregating alleles
if (numHetLoci == 0) {
perc <- 0
} else {
perc <- (numHetQtl / numHetLoci)
}
df <- rbind(df, data.frame(gen=gen,
fitness=mean(twoTraitFitFunc(gv(pop))),
traitVal1=meanG(pop)[1],
traitVal2=meanG(pop)[2],
percFitInc=perc,
segAlleles=numHetLoci,
segQtl=numHetQtl))
# If the population is within n.margin, terminate the simulation
if (mean(twoTraitFitFunc(gv(pop))) >= n.margin) {
break
}
meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
selRat <- selectionRatio(meanFitness)
# Advance the top progeny according to the fitness function
pop <- selectCross(pop=pop, trait=twoTraitFitFunc, nInd=nInd(pop)*selRat, nCrosses=nInd(pop))
}
save_dir <- file.path(output_dir, "BeneficialAlleles")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, "beneficialalleles.pdf")
pdf(fname)
# Plot the ratio of segregating QTL to segregating alleles, along with fitness
par(mar = c(5, 5, 3, 5) + 0.3)
plot(df$gen, df$fitness, type="l", lwd = "3", col=2, xlab = "Generation", ylab = "Fitness")
par(new = TRUE)
plot(df$gen, df$percFitInc, type="l", lwd = "3", col = 3, axes = FALSE, xlab = "", ylab = "")
axis(side = 4, at = pretty(range(df$percFitInc)))
mtext("% of Segregating alleles that are QTL", side = 4, line = 3)
par(xpd=TRUE)
legend("right",
c("Fitness", "% Alleles"),
lty = 1,
lwd = 3,
col = 2:3)
dev.off()
This block runs n.nPops * n.sims Monte Carlo simulations of different
populations to determine the change in the average effect size of
alleles that are fixed along the adaptive walk, saving the order in
which the alleles are fixed n.nPops populations are created, and each
one undergoes n.sims unique adaptive walks

This block will simulate one base population, from which two
sub-populations are selected, and undergo purifying selection
independently.
source("Scripts/GlobalParameters.R")
source("Scripts/CreateFounderPop.R")
fit_df <- data.frame(gen=1:n.burnInGens,
fitness=numeric(n.burnInGens),
traitValA=numeric(n.burnInGens),
traitValB=numeric(n.burnInGens))
pop <- founderPop
# Burn-in generations
for (gen in 1:n.burnInGens) {
fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
fit_df$traitValA[gen] <- meanP(pop)[1]
fit_df$traitValB[gen] <- meanP(pop)[2]
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
}
# Create a random vector of size n.pops, with a random order of sub-population ids
randVec <- sample(rep(c(1:n.nPops), times=n.popSize/n.nPops))
# Select all of the "1" indexed individuals
popA <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=1, randVec=randVec)
# Select all of the "2" indexed individuals
popB <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=2, randVec=randVec)
# Create dataframes for each subpopulation, initializing with current values
popA_df <- data.frame(gen=c(1),
fitness=c(mean(twoTraitFitFunc(pheno(popA)))),
traitValA=c(meanP(popA)[1]),
traitValB=c(meanP(popA)[2]))
popB_df <- data.frame(gen=c(1),
fitness=c(mean(twoTraitFitFunc(pheno(popB)))),
traitValA=c(meanP(popB)[1]),
traitValB=c(meanP(popB)[2]))
# Iterate through the generations
for (gen in 1:n.gens) {
# If popA is within the margin of the fitness optimum, don't progress it any further
if (mean(twoTraitFitFunc(pheno(popA))) < n.margin) {
# Advance the population
meanFitness <- mean(twoTraitFitFunc(pheno(popA)))
# Get a selection ratio based on fitness
selRat <- selectionRatio(meanFitness)
popA <- selectCross(popA, trait=twoTraitFitFunc, nInd=nInd(popA)*selRat, nCrosses=nInd(popA))
# Update the dataframe with new values
popA_df <- rbind(popA_df, data.frame(gen=gen,
fitness=meanFitness,
traitValA=meanP(popA)[1],
traitValB=meanP(popA)[2]))
}
# If popB is within the margin of the fitness optimum, don't progress it any further
if (mean(twoTraitFitFunc(pheno(popB))) < n.margin) {
# If popA is within the margin of the fitness optimum, don't progress it any further
meanFitness <- mean(twoTraitFitFunc(pheno(popB)))
# Get a selection ratio based on fitnes
selRat <- selectionRatio(meanFitness)
popB <- selectCross(popB, trait=twoTraitFitFunc, nInd=nInd(popB)*selRat, nCrosses=nInd(popB))
# Update the dataframe with new values
popB_df <- rbind(popB_df, data.frame(gen=gen,
fitness=meanFitness,
traitValA=meanP(popB)[1],
traitValB=meanP(popB)[2]))
}
}
# Update rownames
rownames(popA_df) <- 1:nrow(popA_df)
rownames(popB_df) <- 1:nrow(popB_df)
# Plot the adaptive walks
fig <- plot_ly()
fig <- add_trace(
fig,
fit_df,
name = "Burn In",
x = fit_df$traitValA,
y = fit_df$traitValB,
z = fit_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = 'yellow',
line = list(width = 5)
)
fig <- add_trace(
fig,
popA_df,
name = "Pop A",
x = popA_df$traitValA,
y = popA_df$traitValB,
z = popA_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = 'red',
line = list(width = 5)
)
fig <- add_trace(
fig,
popB_df,
name = "Pop B",
x = popB_df$traitValA,
y = popB_df$traitValB,
z = popB_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = 'blue',
line = list(width = 5)
)
p <- fig %>%
layout(legend=list(title=list(text='Population')),
showlegend=FALSE,
scene = list(xaxis = list(title = "Trait A"),
yaxis = list(title = "Trait B"),
zaxis = list(title = "Fitness"),
aspectmode='cube')) %>% hide_colorbar()
save_dir <- file.path(output_dir, "DivergingPopulations")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "adaptivewalks.html")
htmlwidgets::saveWidget(as_widget(p), fname)
fname <- file.path(save_dir, "2PopulationFitness.html")
p <- plot3dPopulationFitnessTwoPops(popA, popB)
htmlwidgets::saveWidget(as_widget(p), fname)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, "traitarchitecture.pdf")
pdf(fname)
p1 <- plotTraitArchitecture(popA, "Fitness", "popA")
p2 <- plotTraitArchitecture(popB, "Fitness", "popB")
p3 <- plotTraitArchitecture(popA, "Additive", "popA")
p4 <- plotTraitArchitecture(popB, "Additive", "popB")
(p1|p2)/(p3|p4)
dev.off()
# Create a density plot of trait 1
trait1.df <- as.data.frame(cbind(pheno(popA)[,1], pheno(popB)[,1]))
colnames(trait1.df) <- c("popA", "popB")
trait1.df <- trait1.df %>%
pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t1 <- ggplot(trait1.df, aes(pheno, fill=pop, color=pop)) +
geom_density(alpha=0.1) +
labs(title="Trait 1")
# Create a density plot of trait 2
trait2.df <- as.data.frame(cbind(pheno(popA)[,2], pheno(popB)[,2]))
colnames(trait2.df) <- c("popA", "popB")
trait2.df <- trait2.df %>%
pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t2 <- ggplot(trait2.df, aes(pheno, fill=pop, color=pop)) +
geom_density(alpha=0.1) +
labs(title="Trait 2")
(t1|t2)
ggplot2::ggsave(filename = "trait_distributions.pdf",
path=save_dir,
device = "pdf")
source("Scripts/GlobalParameters.R")
n.qtlPerChr <- 2
n.h2 <- 0.3
n.qtlPerChr <- 2
n.gens <- 100
#source("Scripts/CreateFounderPop.R")
n.selProp <- 0.5
n.subPopSize <- 50
fit_df <- data.frame(gen=1:n.burnInGens,
fitness=numeric(n.burnInGens),
traitValA=numeric(n.burnInGens),
traitValB=numeric(n.burnInGens))
pop <- founderPop
# Burn-in generations
for (gen in 1:n.burnInGens) {
fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
fit_df$traitValA[gen] <- meanP(pop)[1]
fit_df$traitValB[gen] <- meanP(pop)[2]
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
}
# Create a random vector of size n.pops, with a random order of sub-population ids
randVec <- sample(rep(c(1:n.nPops), times=n.popSize/n.nPops))
# Select all of the "1" indexed individuals
popA <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=1, randVec=randVec)
# Select all of the "2" indexed individuals
popB <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=2, randVec=randVec)
# Create dataframes for each subpopulation, initializing with current values
popA_df <- data.frame(gen=c(1),
fitness=c(mean(twoTraitFitFunc(pheno(popA)))),
traitValA=c(meanP(popA)[1]),
traitValB=c(meanP(popA)[2]))
popB_df <- data.frame(gen=c(1),
fitness=c(mean(twoTraitFitFunc(pheno(popB)))),
traitValA=c(meanP(popB)[1]),
traitValB=c(meanP(popB)[2]))
# Iterate through the generations
for (gen in 1:n.gens) {
# If popA is within the margin of the fitness optimum, don't progress it any further
if (mean(twoTraitFitFunc(pheno(popA))) < n.margin) {
# Advance the population
meanFitness <- mean(twoTraitFitFunc(pheno(popA)))
# Get a selection ratio based on fitness
selRat <- selectionRatio(meanFitness)
popA <- selectCross(popA, trait=twoTraitFitFunc, nInd=nInd(popA)*selRat, nCrosses=nInd(popA))
# Update the dataframe with new values
popA_df <- rbind(popA_df, data.frame(gen=gen,
fitness=meanFitness,
traitValA=meanP(popA)[1],
traitValB=meanP(popA)[2]))
}
# If popB is within the margin of the fitness optimum, don't progress it any further
if (mean(twoTraitFitFunc(pheno(popB))) < n.margin) {
# If popA is within the margin of the fitness optimum, don't progress it any further
meanFitness <- mean(twoTraitFitFunc(pheno(popB)))
# Get a selection ratio based on fitnes
selRat <- selectionRatio(meanFitness)
popB <- selectCross(popB, trait=twoTraitFitFunc, nInd=nInd(popB)*selRat, nCrosses=nInd(popB))
# Update the dataframe with new values
popB_df <- rbind(popB_df, data.frame(gen=gen,
fitness=meanFitness,
traitValA=meanP(popB)[1],
traitValB=meanP(popB)[2]))
}
}
# Update rownames
rownames(popA_df) <- 1:nrow(popA_df)
rownames(popB_df) <- 1:nrow(popB_df)
# Plot the adaptive walks
# Create a matrix of fitness values, with a small increment along the x and y axes.
fitness_x = seq(n.initTraitVal*-1,n.initTraitVal*1, by=(n.initTraitVal/100))
fitness_y = seq(n.initTraitVal*-1,n.initTraitVal*1, by=(n.initTraitVal/100))
fitness_z = outer(fitness_x,fitness_y,calculateFitnessTwoTrait)
fig <- plot_ly() %>%
layout(xaxis = list(title = "Trait A", constrain = "domain"),
yaxis = list(title = "Trait B", scaleanchor="x")) %>%
add_trace(
fig,
x=fitness_x,
y=fitness_y,
z=fitness_z,
type='contour',
colors = viridis(n=10),
colorbar=list(title = "Fitness"),
line = list(color = 'black', width = 1),
opacity=1) %>%
add_trace(
fig,
fit_df,
name = "Burn-in",
x = fit_df$traitValA,
y = fit_df$traitValB,
type='scatter',
mode = 'lines',
line = list(color = 'orange', width = 4, dash = 'solid'),
opacity = 1) %>%
add_trace(
fig,
popA_df,
name = "Subpop 1",
x = popA_df$traitValA,
y = popA_df$traitValB,
type='scatter',
mode = 'lines',
line = list(color = 'red', width = 4, dash = 'solid'),
opacity = 1) %>%
add_trace(
fig,
popB_df,
name = "Subpop 2",
x = popB_df$traitValA,
y = popB_df$traitValB,
type='scatter',
mode = 'lines',
line = list(color = 'blue', width = 4, dash = 'solid'),
opacity = 1)
save_dir <- file.path(output_dir, "DivergingPopulations")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "adaptivewalks.html")
htmlwidgets::saveWidget(as_widget(fig), fname)
fname <- file.path(save_dir, "2PopulationFitness.html")
p <- plot3dPopulationFitnessTwoPops(popA, popB)
htmlwidgets::saveWidget(as_widget(p), fname)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, "traitarchitecture.pdf")
pdf(fname)
p1 <- plotTraitArchitecture(popA, "Fitness", "popA")
p2 <- plotTraitArchitecture(popB, "Fitness", "popB")
p3 <- plotTraitArchitecture(popA, "Additive", "popA")
p4 <- plotTraitArchitecture(popB, "Additive", "popB")
(p1|p2)/(p3|p4)
dev.off()
null device
1
# Create a density plot of trait 1
trait1.df <- as.data.frame(cbind(pheno(popA)[,1], pheno(popB)[,1]))
colnames(trait1.df) <- c("popA", "popB")
trait1.df <- trait1.df %>%
pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t1 <- ggplot(trait1.df, aes(pheno, fill=pop, color=pop)) +
geom_density(alpha=0.1) +
labs(title="Trait 1")
# Create a density plot of trait 2
trait2.df <- as.data.frame(cbind(pheno(popA)[,2], pheno(popB)[,2]))
colnames(trait2.df) <- c("popA", "popB")
trait2.df <- trait2.df %>%
pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t2 <- ggplot(trait2.df, aes(pheno, fill=pop, color=pop)) +
geom_density(alpha=0.1) +
labs(title="Trait 2")
(t1|t2)
ggplot2::ggsave(filename = "trait_distributions.pdf",
path=save_dir,
device = "pdf")
Saving 7.29 x 4.51 in image

LS0tCnRpdGxlOiAiQWRhcHRpdmUgV2Fsa3MiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IFRlZCBNb255YWsKZGVzY3JpcHRpb246IFRoaXMgbm90ZWJvb2sgY29udGFpbnMgc2NyaXB0cyBmb3IgdW5kZXJzdGFuZGluZyB0aGUgZHluYW1pY3Mgb2YgYWRhcHRpdmUgd2Fsa3MuCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRX0KcmVxdWlyZSgia25pdHIiKQpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIn4vRG9jdW1lbnRzL0NTVS9SL0JyZWVkaW5nU2ltcyIpCmBgYAoKSW1wb3J0cwpgYGB7cn0KbGlicmFyeShBbHBoYVNpbVIpCmxpYnJhcnkoZGV2dG9vbHMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoc25vdykKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodmlyaWRpcykKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJnZG1jZG9uYWxkL25vdGx5IikKbGlicmFyeShub3RseSkKcm0obGlzdCA9IGxzKCkpCgpzZXQuc2VlZCgxMjMpCgpzb3VyY2UoIkZ1bmN0aW9ucy9GaXRuZXNzLlIiKQpzb3VyY2UoIkZ1bmN0aW9ucy9UcmFpdEFyY2hpdGVjdHVyZS5SIikKc291cmNlKCJTY3JpcHRzL0dsb2JhbFBhcmFtZXRlcnMuUiIpCnNvdXJjZSgiU2NyaXB0cy9DcmVhdGVGb3VuZGVyUG9wLlIiKQoKb3V0cHV0X2RpciA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwgIk91dHB1dCIpCmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZGlyKSkgZGlyLmNyZWF0ZShvdXRwdXRfZGlyKQoKdGhlbWUgPC0gdGhlbWUoCiAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFtaWx5PSJIZWx2ZXRpY2EiLCBzaXplPTEwKSwKICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkhlbHZldGljYSIsIHNpemU9MTApLAogICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkhlbHZldGljYSIsIHNpemU9MTIsIGhqdXN0ID0gMC41KSwKICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseT0iSGVsdmV0aWNhIiwgc2l6ZT02KSwKICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkhlbHZldGljYSIsIHNpemU9NiksCiAgICAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGxpbmV3aWR0aD0wLjA1KSwKICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkhlbHZldGljYSIsIHNpemU9MTAsIGhqdXN0ID0gMC41KSwKICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgIGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKVmlldyBhIGZpdG5lc3MgbGFuZHNjYXBlCmBgYHtyfQpwIDwtIHBsb3RGaXRuZXNzTGFuZHNjYXBlKCkKCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiRml0bmVzc0Z1bmN0aW9ucyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJmaXRuZXNzX2Z1bmN0aW9uLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQpgYGAKClBsb3QgZGlmZmVyZW50IHRyYWl0IGFyY2hpdGVjdHVyZXMgYWNjb3JkaW5nIHRvIGRpZmZlcmVudCBhbGdvcml0aG1zCmBgYHtyfQpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlRyYWl0QXJjaGl0ZWN0dXJlIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCiMgQWRkaXRpdmUgZWZmZWN0cwptZXRob2RUeXBlIDwtICJBZGRpdGl2ZSIKZyA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUoZm91bmRlclBvcCwgbWV0aG9kVHlwZSkKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCJ0cmFpdGFyY2hpdGVjdHVyZV8iLCBtZXRob2RUeXBlLCAiLnBkZiIpLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBFZmZlY3Qgb2YgZWFjaCBhbGxlbGUgb24gZml0bmVzcwptZXRob2RUeXBlIDwtICJGaXRuZXNzIgpnIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShmb3VuZGVyUG9wLCBtZXRob2RUeXBlKQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoInRyYWl0YXJjaGl0ZWN0dXJlXyIsIG1ldGhvZFR5cGUsICIucGRmIiksCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIpCgpwIDwtIHBsb3QzZFBvcHVsYXRpb25GaXRuZXNzKGZvdW5kZXJQb3AsIGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgIjNkcG9wdWxhdGlvbmZpdG5lc3MuaHRtbCIpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwKSwgZm5hbWUpCmBgYAoKU2ltdWxhdGUgc2V2ZXJhbCBhZGFwdGl2ZSB3YWxrcyB3aXRoIGRpZmZlcmVudCBwb3B1bGF0aW9uIHNpemVzCmBgYHtyfQojIERpZmZlcmVudCBzdGFydGluZyBwb3B1bGF0aW9uIHNpemVzCnN1YlBvcFNpemVzIDwtIGMoNTAsNTAwKQpzZWxQcm9wcyA8LSBjKDAuNCwwLjQpCm4uaDIgPC0gMC4zCm4udmFyIDwtIDAuMQpuLnF0bFBlckNociA8LSAyCm4uZ2VucyA8LSA1MAoKIyBEb24ndCB1c2UgYSBTTlAgY2hpcCBmb3IgdGhlc2Ugc2ltdWxhdGlvbnMKYWRkU25wQ2hpcCA8LSBGQUxTRQoKZmlnIDwtIHBsb3RfbHkoKSAlPiUKICBsYXlvdXQobGVnZW5kPWxpc3QodGl0bGU9bGlzdCh0ZXh0PSdQb3B1bGF0aW9uIFNpemUnKSksCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICNoaWRlX2NvbG9yYmFyKCkKCiNzb3VyY2UoIlNjcmlwdHMvQ3JlYXRlRm91bmRlclBvcC5SIikKcG9wIDwtIGZvdW5kZXJQb3AKZm9yIChnZW4gaW4gMTpuLmJ1cm5JbkdlbnMpIHsKICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3ApKm4uYnVybkluU2VsUHJvcCwgbkNyb3NzZXM9bkluZChwb3ApKQp9CgojIEl0ZXJhdGUgdGhyb3VnaCBlYWNoIHBvcHVsYXRpb24gc2l6ZQpmb3IgKHAgaW4gMTpsZW5ndGgoc3ViUG9wU2l6ZXMpKSB7CiAgbi5zdWJQb3BTaXplIDwtIHN1YlBvcFNpemVzW3BdCiAgbi5zZWxQcm9wIDwtIHNlbFByb3BzW3BdCiAgcHJpbnQobi5zdWJQb3BTaXplKQogIGZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uZ2VucywKICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW51bWVyaWMobi5nZW5zKSwKICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1udW1lcmljKG4uZ2VucykpCiAgc3ViUG9wIDwtIHNlbGVjdEluZChwb3AsIG5JbmQ9bi5zdWJQb3BTaXplLCB1c2U9InJhbmQiKQogICMgSXRlcmF0ZSB0aHJvdWdoIHRoZSBnZW5lcmF0aW9ucywgdXBkYXRlIHRoZSByZXN1bHQgZGF0YWZyYW1lLCBhbmQgYWR2YW5jZQogICMgcHJvZ2VueSBiYXNlZCBvbiB0aGUgdHdvIHRyYWl0IGZpdG5lc3MgZnVuY2lvbgogIGZvcihnZW4gaW4gMTpuLmdlbnMpIHsKICAgIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHN1YlBvcCkpKQogICAgZml0X2RmJGZpdG5lc3NbZ2VuXSA8LSBjYWxjdWxhdGVGaXRuZXNzVHdvVHJhaXRNb2RpZmllZChtZWFuUChzdWJQb3ApWzFdLCBtZWFuUChzdWJQb3ApWzJdKQogICAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHN1YlBvcClbMV0KICAgIGZpdF9kZiR0cmFpdFZhbEJbZ2VuXSA8LSBtZWFuUChzdWJQb3ApWzJdCiAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICBzdWJQb3AgPC0gc2VsZWN0Q3Jvc3Moc3ViUG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bi5zdWJQb3BTaXplKnNlbFJhdCwgbkNyb3NzZXM9bi5zdWJQb3BTaXplKQogIH0KICAjIEFkZCBhIHRyYWNlIHRvIHJlcHJlc2VudCB0aGlzIHBvcHVsYXRpb24ncyBhZGFwdGl2ZSB3YWxrCiAgZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKAogICAgZmlnLAogICAgZml0X2RmLAogICAgbmFtZSA9IG4uc3ViUG9wU2l6ZSwKICAgIHggPSBmaXRfZGYkdHJhaXRWYWxBLAogICAgeSA9IGZpdF9kZiR0cmFpdFZhbEIsCiAgICB6ID0gZml0X2RmJGZpdG5lc3MsCiAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICBtb2RlID0gJ2xpbmVzJywKICAgIG9wYWNpdHkgPSAxLAogICAgbGluZSA9IGxpc3QoYXV0b2NvbG9yc2NhbGU9RkFMU0UsIGNvbG9ycz0iUHVCdUduIiwgY29sb3I9cCwgd2hpY2g9Miwgd2lkdGggPSAxMCkKICApCn0KCiMgQ3JlYXRlIGEgbWF0cml4IG9mIGZpdG5lc3MgdmFsdWVzLCB3aXRoIGEgc21hbGwgaW5jcmVtZW50IGFsb25nIHRoZSB4IGFuZCB5IGF4ZXMuCmZpdG5lc3NfeCA9IHNlcShuLmluaXRUcmFpdFZhbCotMSxuLmluaXRUcmFpdFZhbCoxLCBieT0obi5pbml0VHJhaXRWYWwvMTAwKSkKZml0bmVzc195ID0gc2VxKG4uaW5pdFRyYWl0VmFsKi0xLG4uaW5pdFRyYWl0VmFsKjEsIGJ5PShuLmluaXRUcmFpdFZhbC8xMDApKQpmaXRuZXNzX3ogPSBvdXRlcihmaXRuZXNzX3gsZml0bmVzc195LGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKCmZpZyA8LSBmaWcgJT4lCiAgICAgIGFkZF90cmFjZSgKICAgICAgICBmaWcsCiAgICAgICAgeD1maXRuZXNzX3gsCiAgICAgICAgeT1maXRuZXNzX3ksCiAgICAgICAgej1maXRuZXNzX3osCiAgICAgICAgdHlwZT0nc3VyZmFjZScsCiAgICAgICAgY29sb3JiYXI9bGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgY29sb3JzID0gdmlyaWRpcyhuPTEwKSwKICAgICAgICBvcGFjaXR5PTEuMCkKCgpmaWcKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJEaWZmZXJlbnRQb3B1bGF0aW9uU2l6ZXMiKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpzYXZlX2RpciA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIGZvcm1hdChTeXMudGltZSgpLCAiJUZfJUhfJU1fJVMiKSkKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKd3JpdGUudGFibGUoZ2V0UGFyYW1zKCksIGZpbGUucGF0aChzYXZlX2RpciwgInBhcmFtcy50eHQiKSwgY29sLm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSwgc2VwPSI6XHQiKQoKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBwYXN0ZTAoInBvcHVsYXRpb25TaXplc18iLCBzdWJQb3BTaXplc1sxXSwgIiwiLCBzdWJQb3BTaXplc1syXSwgIiwiLCBzdWJQb3BTaXplc1szXSwgIi5odG1sIikpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChmaWcpLCBmbmFtZSkKYGBgCgpPdmVybGF5IGFuIGFkYXB0aXZlIHdhbGsgb3ZlciBhIGZpdG5lc3MgbGFuZHNjYXBlLCBhbmQgc2F2ZSAzRCBhbmQgMkQgdmVyc2lvbnMKYGBge3J9CmZpZyA8LSBwbG90X2x5KCkKZml0X2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5nZW5zLAogICAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG4uZ2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5nZW5zKSkKCnBvcCA8LSBmb3VuZGVyUG9wCgpmb3IoZ2VuIGluIDE6bi5nZW5zKSB7CiAgZml0X2RmJGZpdG5lc3NbZ2VuXSA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhndihwb3ApKSkKICBmaXRfZGYkdHJhaXRWYWxBW2dlbl0gPC0gbWVhbkcocG9wKVsxXQogIGZpdF9kZiR0cmFpdFZhbEJbZ2VuXSA8LSBtZWFuRyhwb3ApWzJdCiAgbWVhbkZpdG5lc3MgPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wKSkpCiAgc2VsUmF0IDwtIHNlbGVjdGlvblJhdGlvKG1lYW5GaXRuZXNzKQogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uLnBvcFNpemUqc2VsUmF0LCBuQ3Jvc3Nlcz1uLnBvcFNpemUpCn0KCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiT3ZlcmxheUFkYXB0aXZlV2FsayIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQoKcGxvdFR5cGUgPC0gIkNPTlRPVVIiCnBjIDwtIG92ZXJsYXlXYWxrT25MYW5kc2NhcGUoZml0X2RmLCB0eXBlPXBsb3RUeXBlLCBjYWxjdWxhdGVGaXRuZXNzVHdvVHJhaXQpCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgcGFzdGUwKCJhZGFwdGl2ZXdhbGtfIiwgcGxvdFR5cGUsICIuaHRtbCIpKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocGMpLCBmbmFtZSkKCnBsb3RUeXBlIDwtICJTVVJGQUNFIgpwcyA8LSBvdmVybGF5V2Fsa09uTGFuZHNjYXBlKGZpdF9kZiwgdHlwZT1wbG90VHlwZSwgY2FsY3VsYXRlRml0bmVzc1R3b1RyYWl0KQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIHBhc3RlMCgiYWRhcHRpdmV3YWxrXyIsIHBsb3RUeXBlLCAiLmh0bWwiKSkKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBzKSwgZm5hbWUpCgp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCmBgYAoKUGxvdCB0aGUgY2hhbmdlIGluIGFsbGVsZSBmcmVxdWVuY3kgb3ZlciB0aW1lCmBgYHtyfQpuLnF0bFBlckNociA8LSAyCnNvdXJjZSgiU2NyaXB0cy9DcmVhdGVGb3VuZGVyUG9wLlIiKQpwb3AgPC0gZm91bmRlclBvcAoKIyBSZXN1bHRzIGRhdGFmcmFtZQpmcmVxLmRmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5nZW5zKQoKIyBHZXQgdGhlIGVmZmVjdCBzaXplcyBvZiBlYWNoIHF0bApxdGxFZmYuZGYgPC0gZ2V0UXRsRWZmZWN0U2l6ZXMocG9wKQoKIyBHZXQgdGhlIG5hbWVzIG9mIGFsbCB0aGUgUVRMcwpxdGwgPC0gY29sbmFtZXMoZ2V0VW5pcXVlUXRsKHBvcCkpCgojIENyZWF0ZSBhIGRhdGFmcmFtZSBvZiBhbGwgemVyb3Mgd2hlcmUgdGhlIGNvbHVtbnMgYXJlIHRoZSBRVEwgaWRzLCBhbmQgdGhlICMgcm93cyBpcyB0aGUgIyBvZiBnZW5lcmF0aW9ucwpxdGwuZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgoMCwgbmNvbD1sZW5ndGgocXRsKSwgbnJvdz1uLmdlbnMpKQpjb2xuYW1lcyhxdGwuZGYpIDwtIHF0bAoKIyBDb21iaW5lIHRoZSBkYXRhZnJhbWVzCmZyZXEuZGYgPC0gY2JpbmQoZnJlcS5kZiwgcXRsLmRmKQoKIyBJdGVyYXRlIHRocm91Z2ggZWFjaCBnZW5lcmF0aW9uCmZvcihnZW4gaW4gMTpuLmdlbnMpIHsKICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICAjIEdldCB0aGUgcXRsIGdlbm90eXBlIGRhdGEKICBxdGxHZW5vIDwtIGdldFVuaXF1ZVF0bChwb3ApCiAgIyBHZXQgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgJzInIGFsbGVsZSBhdCBlYWNoIGxvY3VzCiAgZm9yIChsIGluIDE6bGVuZ3RoKHF0bCkpIHsKICAgICMgaWQgaXMgdGhlIG5hbWUgb2YgdGhlIHF0bCAoY2hyX3NpdGUpCiAgICBpZCA8LSBxdGxbbF0KICAgICMgQSBsaXN0IG9mIGdlbm90eXBlIGRhdGEgZm9yIGVhY2ggaW5kaXZpZHVhbCBpbiB0aGUgcG9wdWxhdGlvbiBhdCB0aGF0IGxvY3VzCiAgICBsb2N1cyA8LSBxdGxHZW5vWyxpZF0KICAgICMgQ2FsY3VsYXRlIHRoZSBhbGxlbGUgZnJlcXVlbmN5IGFzIHRoZSBmcmVxdWVuY3kgb2YgaG9tb3p5Z291cyBpbmRpdmlkdWFscyAoZm9yICdhbGxlbGUnKSArCiAgICAjIDEvMiAqIGZyZXF1ZW5jeSBvZiBoZXRlcm96eWdvdXMgaW5kaXZpZHVhbHMgKGFzc3VtZXMgdGhlIGxvY3VzIGlzIGJpYWxsZWxpYykKICAgIGZyZXEuZGZbZ2VuLGlkXSA8LSAoc3VtKGxvY3VzPT1uLmFsbGVsZSkvbi5wb3BTaXplKSArICgoc3VtKGxvY3VzPT0xKS9uLnBvcFNpemUpLzIpCiAgfQogICMgRGV0ZXJtaW5lIHNlbGVjdGlvbiByYXRpb24gYmFzZWQgb24gZml0bmVzcwogIHNlbFJhdCA8LSBzZWxlY3Rpb25SYXRpbyhtZWFuRml0bmVzcykKICAjQWR2YW5jZSBpbmRpdmlkdWFscwogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uLnBvcFNpemUqc2VsUmF0LCBuQ3Jvc3Nlcz1uLnBvcFNpemUpCn0KCiMgTWFrZSB0aGUgZGF0YWZyYW1lIHRpZHkKZnJlcS5kZjwtIG1lbHQoZnJlcS5kZiwgaWQ9ImdlbiIsIHZhcmlhYmxlLm5hbWU9IlFUTCBJRCIsIHZhbHVlLm5hbWU9IkFsbGVsZSBGcmVxdWVuY3kiKQoKIyBBZGQgdGhlIHF0bCBlZmZlY3Qgc2l6ZSBkYXRhIHRvIHRoZSBkYXRhZnJhbWUKZnJlcS5kZiA8LSBtZXJnZShmcmVxLmRmLCBxdGxFZmYuZGYsIGJ5Lng9IlFUTCBJRCIsIGJ5Lnk9InJvdy5uYW1lcyIsIGFsbC54PVRSVUUpCgojIENyZWF0ZSBhIGxpbmUgcGxvdCBmb3IgdGhlIGNoYW5nZSBpbiBmcmVxdWVuY3kgb2YgYWxsZWxlcyBvdmVyIHRpbWUKIyBFYWNoIGxpbmUncyBvcGFjaXR5IGlzIGEgZnVuY3Rpb24gb2YgaXRzIGVmZmVjdCBzaXplIChoaWdoZXI9ZGFya2VyKQpnIDwtIGdncGxvdChmcmVxLmRmLCBhZXMoeD1nZW4sIHk9ZnJlcSwgY29sb3I9aWQsIGFscGhhPWVmZl9zaXplKSkgKwogIGdlb21fbGluZShzaXplPTAuNykKCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiQWxsZWxlRnJlcXVlbmNpZXMiKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpzYXZlX2RpciA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIGZvcm1hdChTeXMudGltZSgpLCAiJUZfJUhfJU1fJVMiKSkKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKd3JpdGUudGFibGUoZ2V0UGFyYW1zKCksIGZpbGUucGF0aChzYXZlX2RpciwgInBhcmFtcy50eHQiKSwgY29sLm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSwgc2VwPSI6XHQiKQoKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCJhbGxlbGVmcmVxdWVuY2llcy5wZGYiKSwKICAgICAgICAgICAgICAgIHBhdGg9c2F2ZV9kaXIsCiAgICAgICAgICAgICAgICBkZXZpY2UgPSAicGRmIiwKICAgICAgICAgICAgICAgIHdpZHRoPTEwLAogICAgICAgICAgICAgICAgaGVpZ2h0PTcpCmBgYAoKU2ltdWxhdGUgYW4gYWRhcHRpdmUgd2FsaywgYW5kIHBsb3QgdGhlIGZvbGxvd2luZyBjdXJ2ZToKI3NlZ3JlZ2F0aW5nIFFUTC8jc2VncmVnYXRpbmcgbG9jaQoKT3JpZ2luYWwgcXVlc3Rpb246IG91dCBvZiBzZWdyZWdhdGluZyBhbGxlbGVzIGxlZnQgaW4gdGhlIHBvcHVsYXRpb24sIGhvdyBtYW55Cm9mIHRoZW0gd291bGQgaW5jcmVhc2UgZml0bmVzcz8gSXMgdGhpcyBldmVuIGEgdmFsaWQgcXVlc3Rpb24/CgpUT0RPOgotIEhvdyBkb2VzIHRoaXMgY2hhbmdlIHdpdGggc2l6ZSBvZiBhbGxlbGU/Ci0gSG93IGRvZXMgc2l6ZSBvZiAnZml0bmVzcyBkZWx0YScgY2hhbmdlIG92ZXIgZ2VuZXJhdGlvbnM/Ci0gU2ltdWFsYXRlIG11dGF0aW9ucyBmb3IgYSBzaW5nbGUgaW5kaXZpZHVhbCB0byBmaWd1cmUgb3V0IFAoYWxsZWxpYyBzdWJzdGl0dXRpb24gaXMgZmF2b3JhYmxlKSwKc2hvdWxkIHJlZmxlY3QgRktPCi0gUChtdXRhdGlvbiBnZXRzIGZpeGVkKSAtIHNob3cgdGhhdCB0aGlzIG1hdGNoZXMgS2ltdXJhCi0gQ29tcGFyZSBhZGFwdGl2ZSB3YWxrcyBmb3IgREgvaW5icmVkIHBvcHVsYXRpb24gd2hlcmUgdGhlcmUgYXJlIG5ldyBtdXRhdGlvbnMgVlMKcG9wdWxhdGlvbiB3L3N0YW5kaW5nIGdlbmV0aWMgdmFyaWF0aW9uCmBgYHtyfQpuLmdlbnMgPC0gMjAwCgojIFJlc2V0IHZhcmlhYmxlcwpzb3VyY2UoIlNjcmlwdHMvQ3JlYXRlRm91bmRlclBvcC5SIikKc291cmNlKCJTY3JpcHRzL0dsb2JhbFBhcmFtZXRlcnMuUiIpCgpwb3AgPC0gZm91bmRlclBvcApkZiA8LSBkYXRhLmZyYW1lKGdlbj1jKCksCiAgICAgICAgICAgICAgICAgZml0bmVzcz1jKCksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWwxPWMoKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbDI9YygpLAogICAgICAgICAgICAgICAgIHBlcmNGaXRJbmM9YygpLAogICAgICAgICAgICAgICAgIHNlZ0FsbGVsZXM9YygpLAogICAgICAgICAgICAgICAgIHNlZ1F0bD1jKCkpCgojIEl0ZXJhdGUgdGhyb3VnaCB0aGUgZ2VuZXJhdGlvbnMKZm9yIChnZW4gaW4gMTpuLmdlbnMpIHsKICBwcmludChnZW4pCiAgIyBDb3VudGVyIHZhcmlhYmxlcwogIG51bUhldExvY2kgPSAwCiAgbnVtSGV0UXRsID0gMAogIG51bUluYyA9IDAKCiAgIyBHZXQgYWxsIG9mIHRoZSBsb2NpIGZyb20gdGhlIHBvcHVsYXRpb24KICBzZWdTaXRlR2VubyA8LSBwdWxsU2VnU2l0ZUdlbm8ocG9wKQogICMgR2V0IGFsbCBvZiB0aGUgUVRMIGZyb20gdGhlIHBvcHVsYXRpb24gKGEgc2FtcGxpbmcgb2YgdGhlIHNlZ1NpdGVHZW5vKQogIHF0bEdlbm8gPC0gZ2V0VW5pcXVlUXRsKHBvcCkKICBxdGxMb2NpIDwtIGNvbG5hbWVzKHF0bEdlbm8pCiAgbG9jaSA8LSBjb2xuYW1lcyhzZWdTaXRlR2VubykKICBuTG9jaSA8LSBuY29sKHNlZ1NpdGVHZW5vKQogICMgSXRlcmF0ZSB0aHJvdWdoIGFsbCBvZiB0aGUgbG9jaQogICMgVE9ETzoganVzdCBjYWxjdWxhdGUgbnVtSGV0UXRsIC8gbnVtSGV0TG9jaQogIGZvciAobCBpbiAxOm5jb2woc2VnU2l0ZUdlbm8pKSB7CiAgICBpZCA8LSBsb2NpW2xdCiAgICBsb2N1cyA9IHNlZ1NpdGVHZW5vWyxsXQogICAgIyBDaGVjayBpZiB0aGUgbG9jdXMgaXMgc2VncmVnYXRpbmcKICAgIGlmIChoZXRMb2N1cyhsb2N1cykpIHsKICAgICAgIyBJbmNyZW1lbnQgdGhlIGNvdW50ZXIKICAgICAgbnVtSGV0TG9jaSA8LSBudW1IZXRMb2NpICsgMQogICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUgLSB0aGlzIGlzIHdoZXJlIHRoZSBidWcgaXMKICAgICAgI2UgPC0gZ2V0RWZmZWN0U2l6ZShsb2N1cywgaWQsIHBvcCwgIkZpdG5lc3MiKQogICAgICAjaWYgKGUgPiAwKSB7CiAgICAgICMgIG51bUluYyA8LSBudW1JbmMgKzEKICAgICAgI30KICAgICAgIyBJZiB0aGUgbG9jdXMgaXMgYSBRVEwsIHRoZW4gaXQgaGFzIGFuIGVmZmVjdCBzaXplCiAgICAgIGlmIChpZCAlaW4lIHF0bExvY2kpIHsKICAgICAgICBudW1IZXRRdGwgPC0gbnVtSGV0UXRsICsgMQogICAgICB9CiAgICB9CiAgfQoKICAjIENhbGN1bGF0ZSB0aGUgcmF0aW8gb2Ygc2VncmVnYXRpbmcgUVRMIHRvIHNlZ3JlZ2F0aW5nIGFsbGVsZXMKICBpZiAobnVtSGV0TG9jaSA9PSAwKSB7CiAgICBwZXJjIDwtIDAKICB9IGVsc2UgewogICAgcGVyYyA8LSAobnVtSGV0UXRsIC8gbnVtSGV0TG9jaSkKICB9CiAgZGYgPC0gcmJpbmQoZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXRuZXNzPW1lYW4odHdvVHJhaXRGaXRGdW5jKGd2KHBvcCkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbDE9bWVhbkcocG9wKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbDI9bWVhbkcocG9wKVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjRml0SW5jPXBlcmMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnQWxsZWxlcz1udW1IZXRMb2NpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ1F0bD1udW1IZXRRdGwpKQogICMgSWYgdGhlIHBvcHVsYXRpb24gaXMgd2l0aGluIG4ubWFyZ2luLCB0ZXJtaW5hdGUgdGhlIHNpbXVsYXRpb24KICBpZiAobWVhbih0d29UcmFpdEZpdEZ1bmMoZ3YocG9wKSkpID49IG4ubWFyZ2luKSB7CiAgICBicmVhawogIH0KICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgIyBBZHZhbmNlIHRoZSB0b3AgcHJvZ2VueSBhY2NvcmRpbmcgdG8gdGhlIGZpdG5lc3MgZnVuY3Rpb24KICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wPXBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wKSkKfQoKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJCZW5lZmljaWFsQWxsZWxlcyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJiZW5lZmljaWFsYWxsZWxlcy5wZGYiKQpwZGYoZm5hbWUpCgojIFBsb3QgdGhlIHJhdGlvIG9mIHNlZ3JlZ2F0aW5nIFFUTCB0byBzZWdyZWdhdGluZyBhbGxlbGVzLCBhbG9uZyB3aXRoIGZpdG5lc3MKcGFyKG1hciA9IGMoNSwgNSwgMywgNSkgKyAwLjMpCnBsb3QoZGYkZ2VuLCBkZiRmaXRuZXNzLCB0eXBlPSJsIiwgbHdkID0gIjMiLCBjb2w9MiwgeGxhYiA9ICJHZW5lcmF0aW9uIiwgeWxhYiA9ICJGaXRuZXNzIikKcGFyKG5ldyA9IFRSVUUpIApwbG90KGRmJGdlbiwgZGYkcGVyY0ZpdEluYywgdHlwZT0ibCIsIGx3ZCA9ICIzIiwgY29sID0gMywgYXhlcyA9IEZBTFNFLCB4bGFiID0gIiIsIHlsYWIgPSAiIikgCmF4aXMoc2lkZSA9IDQsIGF0ID0gcHJldHR5KHJhbmdlKGRmJHBlcmNGaXRJbmMpKSkKbXRleHQoIiUgb2YgU2VncmVnYXRpbmcgYWxsZWxlcyB0aGF0IGFyZSBRVEwiLCBzaWRlID0gNCwgbGluZSA9IDMpCnBhcih4cGQ9VFJVRSkKbGVnZW5kKCJyaWdodCIsCiAgYygiRml0bmVzcyIsICIlIEFsbGVsZXMiKSwKICAgICAgIGx0eSA9IDEsCiAgICAgICBsd2QgPSAzLAogICAgICAgY29sID0gMjozKQpkZXYub2ZmKCkKYGBgCgpUaGlzIGJsb2NrIHJ1bnMgbi5uUG9wcyAqIG4uc2ltcyBNb250ZSBDYXJsbyBzaW11bGF0aW9ucyBvZiBkaWZmZXJlbnQgcG9wdWxhdGlvbnMgdG8gZGV0ZXJtaW5lCnRoZSBjaGFuZ2UgaW4gdGhlIGF2ZXJhZ2UgZWZmZWN0IHNpemUgb2YgYWxsZWxlcyB0aGF0IGFyZSBmaXhlZCBhbG9uZyB0aGUgYWRhcHRpdmUgd2FsaywKc2F2aW5nIHRoZSBvcmRlciBpbiB3aGljaCB0aGUgYWxsZWxlcyBhcmUgZml4ZWQKbi5uUG9wcyBwb3B1bGF0aW9ucyBhcmUgY3JlYXRlZCwgYW5kIGVhY2ggb25lIHVuZGVyZ29lcyBuLnNpbXMgdW5pcXVlIGFkYXB0aXZlIHdhbGtzCgpgYGB7cn0Kbi5zaW1zIDwtIDIwMApuLnBvcFJlc2V0cyA8LSA1Cm4uZ2VucyA8LSAyMDAKbi5zZWxQcm9wIDwtIDAuMQpuLmgyIDwtIDAuMgpuLnN1YlBvcFNpemUgPC0gNTAwCm4ucXRsUGVyQ2hyIDwtIDIKbi5tYXJnaW4gPC0gMAphZGRTbnBDaGlwIDwtIEZBTFNFCgppZiAoc2F2ZUZpdG5lc3NQbG90cykgewogIGZpZyA8LSBwbG90X2x5KCkKfQoKIyBUaWR5IGRhdGFmcmFtZSB0byBzdG9yZSB0aGUgb3JkZXIgaW4gd2hpY2ggZWFjaCBhbGxlbGUgaXMgZml4ZWQsIGFuZCB0aGUgZWZmZWN0IHNpemUKZWZmU2l6ZS5kZiA8LSBkYXRhLmZyYW1lKG9yZGVyRml4ZWQ9YygpLAogICAgICAgICAgICAgICAgICAgICAgICAgZ2VuPWMoKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9YygpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFNpemU9YygpKQojIENyZWF0ZSBhIG5ldyBwb3B1bGF0aW9uIG4ublBvcHMgdGltZXMKZm9yIChwIGluIDE6bi5wb3BSZXNldHMpIHsKICBwcmludChwYXN0ZTAoIlBvcCBSZXNldDogIiwgcCkpCiAgc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCgogICMgR2V0IHRoZSBlZmZlY3Qgc2l6ZXMgb2YgZWFjaCBxdGwKICBxdGxFZmYuZGYgPC0gZ2V0UXRsRWZmZWN0U2l6ZXMoZm91bmRlclBvcCkKICAKICAjIEdldCB0aGUgbmFtZXMgb2YgYWxsIHRoZSBRVExzCiAgcXRsIDwtIGNvbG5hbWVzKGdldFVuaXF1ZVF0bChmb3VuZGVyUG9wKSkKICBmb3IgKHMgaW4gMTpuLnNpbXMpIHsKICAgIHBvcCA8LSBmb3VuZGVyUG9wCiAgICBpZiAoc2F2ZUZpdG5lc3NQbG90cykgewogICAgICBwb3BfZGYgPC0gZGF0YS5mcmFtZShnZW49MTpuLmdlbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICBmaXRuZXNzPW51bWVyaWMobi5nZW5zKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1udW1lcmljKG4uZ2VucyksCiAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bnVtZXJpYyhuLmdlbnMpKQogICAgfQogICAgCiAgCiAgICBwcmludChwYXN0ZTAoIlNpbTogIiwgcykpCiAgICAjIGlkeCBpcyB0aGUgb3JkZXIgaW4gd2hpY2ggYW4gYWxsZWxlIGlzIGZpeGVkIGFsb25nIGFuIGFkYXB0aXZlIHdhbGsKICAgIGlkeCA8LSAxCiAgICAjIHdoZXRoZXIgb3Igbm90IHRvIGluY3JlbWVudCB0aGUgaWR4IGNvdW50ZXIuIE11bHRpcGxlIGFsbGVsZXMgbWF5IGJlIGZpeGVkCiAgICAjIGluIHRoZSBzYW1lIGdlbmVyYXRpb24sIHNvIHRoaXMgY2Fubm90IGJlIGluY3JlbWVudGVkIHVudGlsIGVhY2ggbG9jdXMKICAgICMgaGFzIGJlZW4gZXhhbWluZWQKICAgIGluYyA8LSBGQUxTRQogICAgCiAgICAjIEJ1cm4taW4KICAgIGZvciAoZ2VuIGluIDE6bi5idXJuSW5HZW5zKSB7CiAgICAgIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcCkqbi5idXJuSW5TZWxQcm9wLCBuQ3Jvc3Nlcz1uSW5kKHBvcCkpCiAgICB9CgogICAgIyBTZWxlY3QgYSBzdWJwb3B1bGF0aW9uCiAgICBwb3AgPC0gc2VsZWN0SW5kKHBvcCwgbkluZD1uLnN1YlBvcFNpemUsIHVzZT0icmFuZCIpCiAgICAKICAgICMgTWFpbiBzaW11bGF0aW9uCiAgICBmb3IgKGdlbiBpbiAxOm4uZ2VucykgewogICAgICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICAgICAgc2VsUmF0IDwtIHNlbGVjdGlvblJhdGlvKG1lYW5GaXRuZXNzKQogICAgICBpZiAoc2F2ZUZpdG5lc3NQbG90cykgewogICAgICAgIHBvcF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbkZpdG5lc3MKICAgICAgICBwb3BfZGYkdHJhaXRWYWxBW2dlbl0gPC0gbWVhblAocG9wKVsxXQogICAgICAgIHBvcF9kZiR0cmFpdFZhbEJbZ2VuXSA8LSBtZWFuUChwb3ApWzJdCiAgICAgIH0KICAgICAgIyBLZWVwIHRyYWNrIG9mIHRoZSBjdXJyZW50IHBvcHVsYXRpb24KICAgICAgcHJldlBvcCA8LSBwb3AKICAgICAgIyBBZHZhbmNlIHRoZSBwb3B1bGF0aW9uIGJhc2VkIG9uIGZpdG5lc3MKICAgICAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wKSkKICAgICAgIyBHZXQgdGhlIHF0bCBnZW5vdHlwZXMgZnJvbSB0aGUgY3VycmVudCBhbmQgbmV3IHBvcHVsYXRpb25zLCBzbyB3ZSBjYW4gY29tcGFyZSB0aGVtCiAgICAgICMgRGV0ZXJtaW5lIHdoZXRoZXIgZWFjaCBsb2N1cyBpcyBzZWdyZWdhdGluZwogICAgICBwcmV2SGV0IDwtIGFzLmRhdGEuZnJhbWUoYXBwbHkoZ2V0VW5pcXVlUXRsKHByZXZQb3ApLCBNQVJHSU49MiwgRlVOPWhldExvY3VzKSkKICAgICAgY3VySGV0IDwtIGFzLmRhdGEuZnJhbWUoYXBwbHkoZ2V0VW5pcXVlUXRsKHBvcCksIE1BUkdJTj0yLCBGVU49aGV0TG9jdXMpKQogICAgICAjIEpvaW4gdGhlIHR3byBzZXRzIG9mIGdlbmV0aWMgZGF0YQogICAgICBsb2NpIDwtIGNiaW5kKHByZXZIZXQsIGN1ckhldCkKICAgICAgY29sbmFtZXMobG9jaSkgPC0gYygicHJldiIsICJjdXIiKQogICAgICAjIEFkZCBhIGNvbHVtbiBjYWxsZWQgImZpeGVkIiB3aGljaCBpcyB0cnVlIGlmIHRoZSBwcmV2aW91cyBnZW5vdHlwZSB3YXMgc2VncmVnYXRpbmcKICAgICAgIyBhbmQgdGhlIGN1cnJlbnQgZ2Vub3R5cGUgaXMgbm90CiAgICAgIGxvY2kgPC0gbG9jaSAlPiUKICAgICAgICBtdXRhdGUoZml4ZWQ9bWFwcGx5KGZ1bmN0aW9uKHAsYykgKHAgJiYgIWMpLCBwcmV2LCBjdXIpKQogICAgICAKICAgICAgIyBJdGVyYXRlIHRocm91Z2ggYWxsIGxvY2ksIGFuZCBpZiB0aGUgbG9jdXMgd2FzIGZpeGVkIHRoaXMgZ2VuZXJhdGlvbiwKICAgICAgIyBhZGQgaXQgdG8gdGhlIHJlc3VsdCBkYXRhZnJhbWUKICAgICAgZm9yIChsIGluIDE6bGVuZ3RoKHF0bCkpIHsKICAgICAgICBpZCA8LSBxdGxbbF0KICAgICAgICBpZiAobG9jaVtpZCwgImZpeGVkIl0gPT0gVFJVRSkgewogICAgICAgICAgIyBJbmNyZW1lbnQgdGhlIG9yZGVyIGNvdW50ZXIgYWZ0ZXIgdGhpcyBnZW5lcmF0aW9uCiAgICAgICAgICBpbmMgPC0gVFJVRQogICAgICAgICAgZWZmU2l6ZS5kZiA8LSByYmluZChlZmZTaXplLmRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKG9yZGVyRml4ZWQ9YyhpZHgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbj1jKGdlbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1jKG1lYW5GaXRuZXNzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RTaXplPWMocXRsRWZmLmRmW2lkLDFdKSkpCiAgICAgICAgfQogICAgICB9CiAgICAgICMgQ2hlY2sgd2hldGhlciB0byBpbmNyZW1lbnQgdGhlIG9yZGVyIGNvdW50ZXIgYW5kIHJlc2V0ICdpbmMnCiAgICAgIGlmIChpbmMpIHsKICAgICAgICBpZHggPC0gaWR4ICsgMQogICAgICAgIGluYyA8LSBGQUxTRQogICAgICB9CiAgICAgICMgSWYgYWxsIGFsbGVsZXMgYXJlIGZpeGVkLCB0ZXJtaW5hdGUgdGhlIHNpbXVsYXRpb24KICAgICAgaWYoIWFueShjdXJIZXQpKSB7CiAgICAgICAgYnJlYWsKICAgICAgfQogICAgICAjIElmIHRoZSBwb3B1bGF0aW9uIGlzIHdpdGhpbiBuLm1hcmdpbiwgdGVybWluYXRlIHRoZSBzaW11bGF0aW9uCiAgICAgIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkgPj0gbi5tYXJnaW4pIHsKICAgICAgICBicmVhawogICAgICB9CiAgICAgIAogICAgfQogICAgIyBBZGQgdGhlIGFkYXB0aXZlIHdhbGsgb2YgdGhlIHN1Yi1wb3B1bGF0aW9uCiAgICBpZiAoc2F2ZUZpdG5lc3NQbG90cykgewogICAgICBmaWcgPC0gYWRkX3RyYWNlKAogICAgICAgIGZpZywKICAgICAgICBwb3BfZGYsCiAgICAgICAgbmFtZSA9IHMsCiAgICAgICAgeCA9IHBvcF9kZiR0cmFpdFZhbEEsCiAgICAgICAgeSA9IHBvcF9kZiR0cmFpdFZhbEIsCiAgICAgICAgeiA9IHBvcF9kZiRmaXRuZXNzLAogICAgICAgIHR5cGUgPSAnc2NhdHRlcjNkJywKICAgICAgICBtb2RlID0gJ2xpbmVzJywKICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICBjb2xvciA9IHMsCiAgICAgICAgbGluZSA9IGxpc3Qod2lkdGggPSAyKQogICAgICApCiAgICB9CiAgfQp9CgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkF2ZXJhZ2VFZmZlY3RTaXplIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKd3JpdGUudGFibGUoZWZmU2l6ZS5kZiwgZmlsZS5wYXRoKHNhdmVfZGlyLCAiZWZmU2l6ZS5jc3YiKSwgY29sLm5hbWVzPVRSVUUsIHF1b3RlPUZBTFNFLCBzZXA9IiwiKQoKIyBEZXRlcm1pbmUgdGhlIGF2ZXJhZ2UgYWRkaXRpdmUgZWZmZWN0IHNpemUgYXQgZWFjaCAnc3RlcCcKZ3JvdXBlZEVmZlNpemUuZGYgPC0gZWZmU2l6ZS5kZiAlPiUKICBtdXRhdGUoc21vb3RoZWRGaXQ9cm91bmQoZml0bmVzcyxkaWdpdHM9MSkvLTQpICU+JQogIGdyb3VwX2J5KHNtb290aGVkRml0KSAlPiUKICBmaWx0ZXIoIShhYnMoZWZmZWN0U2l6ZSAtIG1lZGlhbihlZmZlY3RTaXplKSkgPiAyKnNkKGVmZmVjdFNpemUpKSkgJT4lCiAgc3VtbWFyaXplKG1lYW5FZmZlY3RTaXplID0gbWVhbihlZmZlY3RTaXplKSwgbj1uKCkpICU+JQogIGZpbHRlcihuID4gMTAwKQoKZyA8LSBnZ3Bsb3QoZ3JvdXBlZEVmZlNpemUuZGYsIGFlcyh4PXNtb290aGVkRml0LCB5PW1lYW5FZmZlY3RTaXplKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeF9yZXZlcnNlKCkgKwogIHRoZW1lICsKICBsYWJzKHRpdGxlPSJGaWd1cmUgMSIsIHg9Ik5vcm1hbGl6ZWQgRGlzdGFuY2UgZnJvbSBGaXRuZXNzIE9wdGltdW0iLCB5PSJNZWFuIEVmZmVjdCBTaXplIikgKwogIGdlb21fc21vb3RoKGZvcm11bGEgPSB5IH4geCkKCmdncGxvdDI6Omdnc2F2ZShmaWxlbmFtZSA9ICJhdmVyYWdlX2VmZmVjdF9zaXplX2FkZGl0aXZlLnBkZiIsCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIpCgojIENyZWF0ZSBhIHBsb3Qgd2l0aCB0aGUgYWRhcHRpdmUgd2Fsa3MKaWYgKHNhdmVGaXRuZXNzUGxvdHMpIHsKICBwIDwtIGZpZyAlPiUKICAgIGxheW91dChsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1BvcHVsYXRpb24nKSksCiAgICAgICAgICAgc2hvd2xlZ2VuZD1GQUxTRSwKICAgICAgICAgICBzY2VuZSA9IGxpc3QoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIkZpdG5lc3MiKSwKICAgICAgICAgICAgICAgICAgICAgICAgYXNwZWN0bW9kZT0nY3ViZScpKSAlPiUgaGlkZV9jb2xvcmJhcigpCiAgZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAiYWRhcHRpdmV3YWxrLmh0bWwiKQogIGh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwKSwgZm5hbWUpCn0KYGBgCgpUaGlzIGJsb2NrIHdpbGwgc2ltdWxhdGUgb25lIGJhc2UgcG9wdWxhdGlvbiwgZnJvbSB3aGljaCB0d28gc3ViLXBvcHVsYXRpb25zIGFyZSBzZWxlY3RlZCwgYW5kIHVuZGVyZ28gcHVyaWZ5aW5nIHNlbGVjdGlvbiBpbmRlcGVuZGVudGx5LgpgYGB7cn0Kc291cmNlKCJTY3JpcHRzL0dsb2JhbFBhcmFtZXRlcnMuUiIpCnNvdXJjZSgiU2NyaXB0cy9DcmVhdGVGb3VuZGVyUG9wLlIiKQoKZml0X2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5idXJuSW5HZW5zLAogICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuLmJ1cm5JbkdlbnMpLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1udW1lcmljKG4uYnVybkluR2VucyksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5idXJuSW5HZW5zKSkKcG9wIDwtIGZvdW5kZXJQb3AKCiMgQnVybi1pbiBnZW5lcmF0aW9ucwpmb3IgKGdlbiBpbiAxOm4uYnVybkluR2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wKSkpCiAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHBvcClbMV0KICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wKVsyXQogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcCkqbi5idXJuSW5TZWxQcm9wLCBuQ3Jvc3Nlcz1uSW5kKHBvcCkpCn0KCiMgQ3JlYXRlIGEgcmFuZG9tIHZlY3RvciBvZiBzaXplIG4ucG9wcywgd2l0aCBhIHJhbmRvbSBvcmRlciBvZiBzdWItcG9wdWxhdGlvbiBpZHMKcmFuZFZlYyA8LSBzYW1wbGUocmVwKGMoMTpuLm5Qb3BzKSwgdGltZXM9bi5wb3BTaXplL24ublBvcHMpKQoKIyBTZWxlY3QgYWxsIG9mIHRoZSAiMSIgaW5kZXhlZCBpbmRpdmlkdWFscwpwb3BBIDwtIHNlbGVjdEluZChwb3AsIHRyYWl0PXNlbGVjdFN1YlBvcCwgc2VsZWN0VG9wPVRSVUUsIG5JbmQ9bi5zdWJQb3BTaXplLCBpZHg9MSwgcmFuZFZlYz1yYW5kVmVjKQojIFNlbGVjdCBhbGwgb2YgdGhlICIyIiBpbmRleGVkIGluZGl2aWR1YWxzCnBvcEIgPC0gc2VsZWN0SW5kKHBvcCwgdHJhaXQ9c2VsZWN0U3ViUG9wLCBzZWxlY3RUb3A9VFJVRSwgbkluZD1uLnN1YlBvcFNpemUsIGlkeD0yLCByYW5kVmVjPXJhbmRWZWMpCgojIENyZWF0ZSBkYXRhZnJhbWVzIGZvciBlYWNoIHN1YnBvcHVsYXRpb24sIGluaXRpYWxpemluZyB3aXRoIGN1cnJlbnQgdmFsdWVzCnBvcEFfZGYgPC0gZGF0YS5mcmFtZShnZW49YygxKSwKICAgICAgICAgICAgICAgICBmaXRuZXNzPWMobWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQSkpKSksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPWMobWVhblAocG9wQSlbMV0pLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1jKG1lYW5QKHBvcEEpWzJdKSkKcG9wQl9kZiA8LSBkYXRhLmZyYW1lKGdlbj1jKDEpLAogICAgICAgICAgICAgICAgIGZpdG5lc3M9YyhtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BCKSkpKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9YyhtZWFuUChwb3BCKVsxXSksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPWMobWVhblAocG9wQilbMl0pKQoKIyBJdGVyYXRlIHRocm91Z2ggdGhlIGdlbmVyYXRpb25zCmZvciAoZ2VuIGluIDE6bi5nZW5zKSB7CiAgIyBJZiBwb3BBIGlzIHdpdGhpbiB0aGUgbWFyZ2luIG9mIHRoZSBmaXRuZXNzIG9wdGltdW0sIGRvbid0IHByb2dyZXNzIGl0IGFueSBmdXJ0aGVyCiAgaWYgKG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEEpKSkgPCBuLm1hcmdpbikgewogICAgIyBBZHZhbmNlIHRoZSBwb3B1bGF0aW9uCiAgICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BBKSkpCiAgICAjIEdldCBhIHNlbGVjdGlvbiByYXRpbyBiYXNlZCBvbiBmaXRuZXNzCiAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICBwb3BBIDwtIHNlbGVjdENyb3NzKHBvcEEsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcEEpKnNlbFJhdCwgbkNyb3NzZXM9bkluZChwb3BBKSkKICAgICMgVXBkYXRlIHRoZSBkYXRhZnJhbWUgd2l0aCBuZXcgdmFsdWVzCiAgICBwb3BBX2RmIDwtIHJiaW5kKHBvcEFfZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1tZWFuRml0bmVzcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW1lYW5QKHBvcEEpWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bWVhblAocG9wQSlbMl0pKQogICAgCiAgfQogICMgSWYgcG9wQiBpcyB3aXRoaW4gdGhlIG1hcmdpbiBvZiB0aGUgZml0bmVzcyBvcHRpbXVtLCBkb24ndCBwcm9ncmVzcyBpdCBhbnkgZnVydGhlcgogIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BCKSkpIDwgbi5tYXJnaW4pIHsKICAgICMgSWYgcG9wQSBpcyB3aXRoaW4gdGhlIG1hcmdpbiBvZiB0aGUgZml0bmVzcyBvcHRpbXVtLCBkb24ndCBwcm9ncmVzcyBpdCBhbnkgZnVydGhlcgogICAgbWVhbkZpdG5lc3MgPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQikpKQogICAgIyBHZXQgYSBzZWxlY3Rpb24gcmF0aW8gYmFzZWQgb24gZml0bmVzCiAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICBwb3BCIDwtIHNlbGVjdENyb3NzKHBvcEIsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcEIpKnNlbFJhdCwgbkNyb3NzZXM9bkluZChwb3BCKSkKICAgICMgVXBkYXRlIHRoZSBkYXRhZnJhbWUgd2l0aCBuZXcgdmFsdWVzCiAgICBwb3BCX2RmIDwtIHJiaW5kKHBvcEJfZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1tZWFuRml0bmVzcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW1lYW5QKHBvcEIpWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bWVhblAocG9wQilbMl0pKQogIH0KfQojIFVwZGF0ZSByb3duYW1lcwpyb3duYW1lcyhwb3BBX2RmKSA8LSAxOm5yb3cocG9wQV9kZikKcm93bmFtZXMocG9wQl9kZikgPC0gMTpucm93KHBvcEJfZGYpCgojIFBsb3QgdGhlIGFkYXB0aXZlIHdhbGtzCmZpZyA8LSBwbG90X2x5KCkKZmlnIDwtIGFkZF90cmFjZSgKICBmaWcsCiAgZml0X2RmLAogIG5hbWUgPSAiQnVybiBJbiIsCiAgeCA9IGZpdF9kZiR0cmFpdFZhbEEsCiAgeSA9IGZpdF9kZiR0cmFpdFZhbEIsCiAgeiA9IGZpdF9kZiRmaXRuZXNzLAogIHR5cGUgPSAnc2NhdHRlcjNkJywKICBtb2RlID0gJ2xpbmVzJywKICBvcGFjaXR5ID0gMSwKICBjb2xvciA9ICd5ZWxsb3cnLAogIGxpbmUgPSBsaXN0KHdpZHRoID0gNSkKKQoKZmlnIDwtIGFkZF90cmFjZSgKICAgIGZpZywKICAgIHBvcEFfZGYsCiAgICBuYW1lID0gIlBvcCBBIiwKICAgIHggPSBwb3BBX2RmJHRyYWl0VmFsQSwKICAgIHkgPSBwb3BBX2RmJHRyYWl0VmFsQiwKICAgIHogPSBwb3BBX2RmJGZpdG5lc3MsCiAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICBtb2RlID0gJ2xpbmVzJywKICAgIG9wYWNpdHkgPSAxLAogICAgY29sb3IgPSAncmVkJywKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSkKICApCgpmaWcgPC0gYWRkX3RyYWNlKAogICAgZmlnLAogICAgcG9wQl9kZiwKICAgIG5hbWUgPSAiUG9wIEIiLAogICAgeCA9IHBvcEJfZGYkdHJhaXRWYWxBLAogICAgeSA9IHBvcEJfZGYkdHJhaXRWYWxCLAogICAgeiA9IHBvcEJfZGYkZml0bmVzcywKICAgIHR5cGUgPSAnc2NhdHRlcjNkJywKICAgIG1vZGUgPSAnbGluZXMnLAogICAgb3BhY2l0eSA9IDEsCiAgICBjb2xvciA9ICdibHVlJywKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSkKICApCgoKcCA8LSBmaWcgJT4lCiAgbGF5b3V0KGxlZ2VuZD1saXN0KHRpdGxlPWxpc3QodGV4dD0nUG9wdWxhdGlvbicpKSwKICAgICAgICAgc2hvd2xlZ2VuZD1GQUxTRSwKICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJUcmFpdCBBIiksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQiIpLAogICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIkZpdG5lc3MiKSwKICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSkgJT4lIGhpZGVfY29sb3JiYXIoKQoKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJEaXZlcmdpbmdQb3B1bGF0aW9ucyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJhZGFwdGl2ZXdhbGtzLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQoKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAiMlBvcHVsYXRpb25GaXRuZXNzLmh0bWwiKQpwIDwtIHBsb3QzZFBvcHVsYXRpb25GaXRuZXNzVHdvUG9wcyhwb3BBLCBwb3BCKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJ0cmFpdGFyY2hpdGVjdHVyZS5wZGYiKQpwZGYoZm5hbWUpCgpwMSA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUocG9wQSwgIkZpdG5lc3MiLCAicG9wQSIpCnAyIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BCLCAiRml0bmVzcyIsICJwb3BCIikKcDMgPC0gcGxvdFRyYWl0QXJjaGl0ZWN0dXJlKHBvcEEsICJBZGRpdGl2ZSIsICJwb3BBIikKcDQgPC0gcGxvdFRyYWl0QXJjaGl0ZWN0dXJlKHBvcEIsICJBZGRpdGl2ZSIsICJwb3BCIikKCihwMXxwMikvKHAzfHA0KQpkZXYub2ZmKCkKCiMgQ3JlYXRlIGEgZGVuc2l0eSBwbG90IG9mIHRyYWl0IDEKdHJhaXQxLmRmIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQocGhlbm8ocG9wQSlbLDFdLCBwaGVubyhwb3BCKVssMV0pKQpjb2xuYW1lcyh0cmFpdDEuZGYpIDwtIGMoInBvcEEiLCAicG9wQiIpCnRyYWl0MS5kZiA8LSB0cmFpdDEuZGYgJT4lCiAgcGl2b3RfbG9uZ2VyKGMoInBvcEEiLCAicG9wQiIpLCBuYW1lc190bz0icG9wIiwgdmFsdWVzX3RvPSJwaGVubyIpCnQxIDwtIGdncGxvdCh0cmFpdDEuZGYsIGFlcyhwaGVubywgZmlsbD1wb3AsIGNvbG9yPXBvcCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC4xKSArCiAgbGFicyh0aXRsZT0iVHJhaXQgMSIpCgojIENyZWF0ZSBhIGRlbnNpdHkgcGxvdCBvZiB0cmFpdCAyCnRyYWl0Mi5kZiA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKHBoZW5vKHBvcEEpWywyXSwgcGhlbm8ocG9wQilbLDJdKSkKY29sbmFtZXModHJhaXQyLmRmKSA8LSBjKCJwb3BBIiwgInBvcEIiKQp0cmFpdDIuZGYgPC0gdHJhaXQyLmRmICU+JQogIHBpdm90X2xvbmdlcihjKCJwb3BBIiwgInBvcEIiKSwgbmFtZXNfdG89InBvcCIsIHZhbHVlc190bz0icGhlbm8iKQp0MiA8LSBnZ3Bsb3QodHJhaXQyLmRmLCBhZXMocGhlbm8sIGZpbGw9cG9wLCBjb2xvcj1wb3ApKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuMSkgKwogIGxhYnModGl0bGU9IlRyYWl0IDIiKQoKCih0MXx0MikKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gInRyYWl0X2Rpc3RyaWJ1dGlvbnMucGRmIiwKICAgICAgICAgICAgICAgIHBhdGg9c2F2ZV9kaXIsCiAgICAgICAgICAgICAgICBkZXZpY2UgPSAicGRmIikKCgpgYGAKCmBgYHtyfQpzb3VyY2UoIlNjcmlwdHMvR2xvYmFsUGFyYW1ldGVycy5SIikKbi5xdGxQZXJDaHIgPC0gMgpuLmgyIDwtIDAuMwpuLnF0bFBlckNociA8LSAyCm4uZ2VucyA8LSAxMDAKI3NvdXJjZSgiU2NyaXB0cy9DcmVhdGVGb3VuZGVyUG9wLlIiKQpuLnNlbFByb3AgPC0gMC41Cm4uc3ViUG9wU2l6ZSA8LSA1MAoKZml0X2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5idXJuSW5HZW5zLAogICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuLmJ1cm5JbkdlbnMpLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1udW1lcmljKG4uYnVybkluR2VucyksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5idXJuSW5HZW5zKSkKcG9wIDwtIGZvdW5kZXJQb3AKCiMgQnVybi1pbiBnZW5lcmF0aW9ucwpmb3IgKGdlbiBpbiAxOm4uYnVybkluR2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wKSkpCiAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHBvcClbMV0KICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wKVsyXQogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcCkqbi5idXJuSW5TZWxQcm9wLCBuQ3Jvc3Nlcz1uSW5kKHBvcCkpCn0KCiMgQ3JlYXRlIGEgcmFuZG9tIHZlY3RvciBvZiBzaXplIG4ucG9wcywgd2l0aCBhIHJhbmRvbSBvcmRlciBvZiBzdWItcG9wdWxhdGlvbiBpZHMKcmFuZFZlYyA8LSBzYW1wbGUocmVwKGMoMTpuLm5Qb3BzKSwgdGltZXM9bi5wb3BTaXplL24ublBvcHMpKQoKIyBTZWxlY3QgYWxsIG9mIHRoZSAiMSIgaW5kZXhlZCBpbmRpdmlkdWFscwpwb3BBIDwtIHNlbGVjdEluZChwb3AsIHRyYWl0PXNlbGVjdFN1YlBvcCwgc2VsZWN0VG9wPVRSVUUsIG5JbmQ9bi5zdWJQb3BTaXplLCBpZHg9MSwgcmFuZFZlYz1yYW5kVmVjKQojIFNlbGVjdCBhbGwgb2YgdGhlICIyIiBpbmRleGVkIGluZGl2aWR1YWxzCnBvcEIgPC0gc2VsZWN0SW5kKHBvcCwgdHJhaXQ9c2VsZWN0U3ViUG9wLCBzZWxlY3RUb3A9VFJVRSwgbkluZD1uLnN1YlBvcFNpemUsIGlkeD0yLCByYW5kVmVjPXJhbmRWZWMpCgojIENyZWF0ZSBkYXRhZnJhbWVzIGZvciBlYWNoIHN1YnBvcHVsYXRpb24sIGluaXRpYWxpemluZyB3aXRoIGN1cnJlbnQgdmFsdWVzCnBvcEFfZGYgPC0gZGF0YS5mcmFtZShnZW49YygxKSwKICAgICAgICAgICAgICAgICBmaXRuZXNzPWMobWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQSkpKSksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPWMobWVhblAocG9wQSlbMV0pLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1jKG1lYW5QKHBvcEEpWzJdKSkKcG9wQl9kZiA8LSBkYXRhLmZyYW1lKGdlbj1jKDEpLAogICAgICAgICAgICAgICAgIGZpdG5lc3M9YyhtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BCKSkpKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9YyhtZWFuUChwb3BCKVsxXSksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPWMobWVhblAocG9wQilbMl0pKQoKIyBJdGVyYXRlIHRocm91Z2ggdGhlIGdlbmVyYXRpb25zCmZvciAoZ2VuIGluIDE6bi5nZW5zKSB7CiAgIyBJZiBwb3BBIGlzIHdpdGhpbiB0aGUgbWFyZ2luIG9mIHRoZSBmaXRuZXNzIG9wdGltdW0sIGRvbid0IHByb2dyZXNzIGl0IGFueSBmdXJ0aGVyCiAgaWYgKG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEEpKSkgPCBuLm1hcmdpbikgewogICAgIyBBZHZhbmNlIHRoZSBwb3B1bGF0aW9uCiAgICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BBKSkpCiAgICAjIEdldCBhIHNlbGVjdGlvbiByYXRpbyBiYXNlZCBvbiBmaXRuZXNzCiAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICBwb3BBIDwtIHNlbGVjdENyb3NzKHBvcEEsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcEEpKnNlbFJhdCwgbkNyb3NzZXM9bkluZChwb3BBKSkKICAgICMgVXBkYXRlIHRoZSBkYXRhZnJhbWUgd2l0aCBuZXcgdmFsdWVzCiAgICBwb3BBX2RmIDwtIHJiaW5kKHBvcEFfZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1tZWFuRml0bmVzcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW1lYW5QKHBvcEEpWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bWVhblAocG9wQSlbMl0pKQogICAgCiAgfQogICMgSWYgcG9wQiBpcyB3aXRoaW4gdGhlIG1hcmdpbiBvZiB0aGUgZml0bmVzcyBvcHRpbXVtLCBkb24ndCBwcm9ncmVzcyBpdCBhbnkgZnVydGhlcgogIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BCKSkpIDwgbi5tYXJnaW4pIHsKICAgICMgSWYgcG9wQSBpcyB3aXRoaW4gdGhlIG1hcmdpbiBvZiB0aGUgZml0bmVzcyBvcHRpbXVtLCBkb24ndCBwcm9ncmVzcyBpdCBhbnkgZnVydGhlcgogICAgbWVhbkZpdG5lc3MgPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQikpKQogICAgIyBHZXQgYSBzZWxlY3Rpb24gcmF0aW8gYmFzZWQgb24gZml0bmVzCiAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICBwb3BCIDwtIHNlbGVjdENyb3NzKHBvcEIsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcEIpKnNlbFJhdCwgbkNyb3NzZXM9bkluZChwb3BCKSkKICAgICMgVXBkYXRlIHRoZSBkYXRhZnJhbWUgd2l0aCBuZXcgdmFsdWVzCiAgICBwb3BCX2RmIDwtIHJiaW5kKHBvcEJfZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1tZWFuRml0bmVzcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW1lYW5QKHBvcEIpWzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bWVhblAocG9wQilbMl0pKQogIH0KfQojIFVwZGF0ZSByb3duYW1lcwpyb3duYW1lcyhwb3BBX2RmKSA8LSAxOm5yb3cocG9wQV9kZikKcm93bmFtZXMocG9wQl9kZikgPC0gMTpucm93KHBvcEJfZGYpCgojIFBsb3QgdGhlIGFkYXB0aXZlIHdhbGtzCiMgQ3JlYXRlIGEgbWF0cml4IG9mIGZpdG5lc3MgdmFsdWVzLCB3aXRoIGEgc21hbGwgaW5jcmVtZW50IGFsb25nIHRoZSB4IGFuZCB5IGF4ZXMuCmZpdG5lc3NfeCA9IHNlcShuLmluaXRUcmFpdFZhbCotMSxuLmluaXRUcmFpdFZhbCoxLCBieT0obi5pbml0VHJhaXRWYWwvMTAwKSkKZml0bmVzc195ID0gc2VxKG4uaW5pdFRyYWl0VmFsKi0xLG4uaW5pdFRyYWl0VmFsKjEsIGJ5PShuLmluaXRUcmFpdFZhbC8xMDApKQpmaXRuZXNzX3ogPSBvdXRlcihmaXRuZXNzX3gsZml0bmVzc195LGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKCmZpZyA8LSBwbG90X2x5KCkgJT4lCiAgICAgIGxheW91dCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIsIGNvbnN0cmFpbiA9ICJkb21haW4iKSwKICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJUcmFpdCBCIiwgc2NhbGVhbmNob3I9IngiKSkgJT4lCiAgICAgIGFkZF90cmFjZSgKICAgICAgICBmaWcsCiAgICAgICAgeD1maXRuZXNzX3gsCiAgICAgICAgeT1maXRuZXNzX3ksCiAgICAgICAgej1maXRuZXNzX3osCiAgICAgICAgdHlwZT0nY29udG91cicsCiAgICAgICAgY29sb3JzID0gdmlyaWRpcyhuPTEwKSwKICAgICAgICBjb2xvcmJhcj1saXN0KHRpdGxlID0gIkZpdG5lc3MiKSwKICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIHdpZHRoID0gMSksCiAgICAgICAgb3BhY2l0eT0xKSAlPiUKICAgICAgYWRkX3RyYWNlKAogICAgICAgIGZpZywKICAgICAgICBmaXRfZGYsCiAgICAgICAgbmFtZSA9ICJCdXJuLWluIiwKICAgICAgICB4ID0gZml0X2RmJHRyYWl0VmFsQSwKICAgICAgICB5ID0gZml0X2RmJHRyYWl0VmFsQiwKICAgICAgICB0eXBlPSdzY2F0dGVyJywKICAgICAgICBtb2RlID0gJ2xpbmVzJywKICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICdvcmFuZ2UnLCB3aWR0aCA9IDQsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICBvcGFjaXR5ID0gMSkgJT4lCiAgICAgIGFkZF90cmFjZSgKICAgICAgICBmaWcsCiAgICAgICAgcG9wQV9kZiwKICAgICAgICBuYW1lID0gIlN1YnBvcCAxIiwKICAgICAgICB4ID0gcG9wQV9kZiR0cmFpdFZhbEEsCiAgICAgICAgeSA9IHBvcEFfZGYkdHJhaXRWYWxCLAogICAgICAgIHR5cGU9J3NjYXR0ZXInLAogICAgICAgIG1vZGUgPSAnbGluZXMnLAogICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JlZCcsIHdpZHRoID0gNCwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgIG9wYWNpdHkgPSAxKSAlPiUKICAgICAgYWRkX3RyYWNlKAogICAgICAgIGZpZywKICAgICAgICBwb3BCX2RmLAogICAgICAgIG5hbWUgPSAiU3VicG9wIDIiLAogICAgICAgIHggPSBwb3BCX2RmJHRyYWl0VmFsQSwKICAgICAgICB5ID0gcG9wQl9kZiR0cmFpdFZhbEIsCiAgICAgICAgdHlwZT0nc2NhdHRlcicsCiAgICAgICAgbW9kZSA9ICdsaW5lcycsCiAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAnYmx1ZScsIHdpZHRoID0gNCwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgIG9wYWNpdHkgPSAxKQoKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJEaXZlcmdpbmdQb3B1bGF0aW9ucyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJhZGFwdGl2ZXdhbGtzLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQoZmlnKSwgZm5hbWUpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICIyUG9wdWxhdGlvbkZpdG5lc3MuaHRtbCIpCnAgPC0gcGxvdDNkUG9wdWxhdGlvbkZpdG5lc3NUd29Qb3BzKHBvcEEsIHBvcEIpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwKSwgZm5hbWUpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgInRyYWl0YXJjaGl0ZWN0dXJlLnBkZiIpCnBkZihmbmFtZSkKCnAxIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BBLCAiRml0bmVzcyIsICJwb3BBIikKcDIgPC0gcGxvdFRyYWl0QXJjaGl0ZWN0dXJlKHBvcEIsICJGaXRuZXNzIiwgInBvcEIiKQpwMyA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUocG9wQSwgIkFkZGl0aXZlIiwgInBvcEEiKQpwNCA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUocG9wQiwgIkFkZGl0aXZlIiwgInBvcEIiKQoKKHAxfHAyKS8ocDN8cDQpCmRldi5vZmYoKQoKIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3Qgb2YgdHJhaXQgMQp0cmFpdDEuZGYgPC0gYXMuZGF0YS5mcmFtZShjYmluZChwaGVubyhwb3BBKVssMV0sIHBoZW5vKHBvcEIpWywxXSkpCmNvbG5hbWVzKHRyYWl0MS5kZikgPC0gYygicG9wQSIsICJwb3BCIikKdHJhaXQxLmRmIDwtIHRyYWl0MS5kZiAlPiUKICBwaXZvdF9sb25nZXIoYygicG9wQSIsICJwb3BCIiksIG5hbWVzX3RvPSJwb3AiLCB2YWx1ZXNfdG89InBoZW5vIikKdDEgPC0gZ2dwbG90KHRyYWl0MS5kZiwgYWVzKHBoZW5vLCBmaWxsPXBvcCwgY29sb3I9cG9wKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjEpICsKICBsYWJzKHRpdGxlPSJUcmFpdCAxIikKCiMgQ3JlYXRlIGEgZGVuc2l0eSBwbG90IG9mIHRyYWl0IDIKdHJhaXQyLmRmIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQocGhlbm8ocG9wQSlbLDJdLCBwaGVubyhwb3BCKVssMl0pKQpjb2xuYW1lcyh0cmFpdDIuZGYpIDwtIGMoInBvcEEiLCAicG9wQiIpCnRyYWl0Mi5kZiA8LSB0cmFpdDIuZGYgJT4lCiAgcGl2b3RfbG9uZ2VyKGMoInBvcEEiLCAicG9wQiIpLCBuYW1lc190bz0icG9wIiwgdmFsdWVzX3RvPSJwaGVubyIpCnQyIDwtIGdncGxvdCh0cmFpdDIuZGYsIGFlcyhwaGVubywgZmlsbD1wb3AsIGNvbG9yPXBvcCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC4xKSArCiAgbGFicyh0aXRsZT0iVHJhaXQgMiIpCgoKKHQxfHQyKQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZW5hbWUgPSAidHJhaXRfZGlzdHJpYnV0aW9ucy5wZGYiLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQpgYGAKCg==